home *** CD-ROM | disk | FTP | other *** search
/ Sprite 1984 - 1993 / Sprite 1984 - 1993.iso / src / lib / tk2.3 / dist / tkOption.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-03-16  |  36.5 KB  |  1,329 lines

  1. /* 
  2.  * tkOption.c --
  3.  *
  4.  *    This module contains procedures to manage the option
  5.  *    database, which allows various strings to be associated
  6.  *    with windows either by name or by class or both.
  7.  *
  8.  * Copyright 1990 Regents of the University of California.
  9.  * Permission to use, copy, modify, and distribute this
  10.  * software and its documentation for any purpose and without
  11.  * fee is hereby granted, provided that the above copyright
  12.  * notice appear in all copies.  The University of California
  13.  * makes no representations about the suitability of this
  14.  * software for any purpose.  It is provided "as is" without
  15.  * express or implied warranty.
  16.  */
  17.  
  18. #ifndef lint
  19. static char rcsid[] = "$Header: /user6/ouster/wish/RCS/tkOption.c,v 1.25 92/03/16 08:46:14 ouster Exp $ SPRITE (Berkeley)";
  20. #endif
  21.  
  22. #include "tkConfig.h"
  23. #include "tkInt.h"
  24.  
  25. /*
  26.  * The option database is stored as one tree for each main window.
  27.  * Each name or class field in an option is associated with a node or
  28.  * leaf of the tree.  For example, the options "x.y.z" and "x.y*a"
  29.  * each correspond to three nodes in the tree;  they share the nodes
  30.  * "x" and "x.y", but have different leaf nodes.  One of the following
  31.  * structures exists for each node or leaf in the option tree.  It is
  32.  * actually stored as part of the parent node, and describes a particular
  33.  * child of the parent.
  34.  */
  35.  
  36. typedef struct Element {
  37.     Tk_Uid nameUid;            /* Name or class from one element of
  38.                      * an option spec. */
  39.     union {
  40.     struct ElArray *arrayPtr;    /* If this is an intermediate node,
  41.                      * a pointer to a structure describing
  42.                      * the remaining elements of all
  43.                      * options whose prefixes are the
  44.                      * same up through this element. */
  45.     Tk_Uid valueUid;        /* For leaf nodes, this is the string
  46.                      * value of the option. */
  47.     } child;
  48.     int priority;            /* Used to select among matching
  49.                      * options.  Includes both the
  50.                      * priority level and a serial #.
  51.                      * Greater value means higher
  52.                      * priority.  Irrelevant except in
  53.                      * leaf nodes. */
  54.     int flags;                /* OR-ed combination of bits.  See
  55.                      * below for values. */
  56. } Element;
  57.  
  58. /*
  59.  * Flags in NodeElement structures:
  60.  *
  61.  * CLASS -        Non-zero means this element refers to a class,
  62.  *            Zero means this element refers to a name.
  63.  * NODE -        Zero means this is a leaf element (the child
  64.  *            field is a value, not a pointer to another node).
  65.  *            One means this is a node element.
  66.  * WILDCARD -        Non-zero means this there was a star in the
  67.  *            original specification just before this element.
  68.  *            Zero means there was a dot.
  69.  */
  70.  
  71. #define TYPE_MASK        0x7
  72.  
  73. #define CLASS            0x1
  74. #define NODE            0x2
  75. #define WILDCARD        0x4
  76.  
  77. #define EXACT_LEAF_NAME        0x0
  78. #define EXACT_LEAF_CLASS    0x1
  79. #define EXACT_NODE_NAME        0x2
  80. #define EXACT_NODE_CLASS    0x3
  81. #define WILDCARD_LEAF_NAME    0x4
  82. #define WILDCARD_LEAF_CLASS    0x5
  83. #define WILDCARD_NODE_NAME    0x6
  84. #define WILDCARD_NODE_CLASS    0x7
  85.  
  86. /*
  87.  * The following structure is used to manage a dynamic array of
  88.  * Elements.  These structures are used for two purposes:  to store
  89.  * the contents of a node in the option tree, and for the option
  90.  * stacks described below.
  91.  */
  92.  
  93. typedef struct ElArray {
  94.     int arraySize;        /* Number of elements actually
  95.                  * allocated in the "els" array. */
  96.     int numUsed;        /* Number of elements currently in
  97.                  * use out of els. */
  98.     Element *nextToUse;        /* Pointer to &els[numUsed]. */
  99.     Element els[1];        /* Array of structures describing
  100.                  * children of this node.  The
  101.                  * array will actually contain enough
  102.                  * elements for all of the children
  103.                  * (and even a few extras, perhaps).
  104.                  * This must be the last field in
  105.                  * the structure. */
  106. } ElArray;
  107.  
  108. #define EL_ARRAY_SIZE(numEls) ((unsigned) (sizeof(ElArray) \
  109.     + ((numEls)-1)*sizeof(Element)))
  110. #define INITIAL_SIZE 5
  111.  
  112. /*
  113.  * In addition to the option tree, which is a relatively static structure,
  114.  * there are eight additional structures called "stacks", which are used
  115.  * to speed up queries into the option database.  The stack structures
  116.  * are designed for the situation where an individual widget makes repeated
  117.  * requests for its particular options.  The requests differ only in
  118.  * their last name/class, so during the first request we extract all
  119.  * the options pertaining to the particular widget and save them in a
  120.  * stack-like cache;  subsequent requests for the same widget can search
  121.  * the cache relatively quickly.  In fact, the cache is a hierarchical
  122.  * one, storing a list of relevant options for this widget and all of
  123.  * its ancestors up to the application root;  hence the name "stack".
  124.  *
  125.  * Each of the eight stacks consists of an array of Elements, ordered in
  126.  * terms of levels in the window hierarchy.  All the elements relevant
  127.  * for the top-level widget appear first in the array, followed by all
  128.  * those from the next-level widget on the path to the current widget,
  129.  * etc. down to those for the current widget.
  130.  *
  131.  * Cached information is divided into eight stacks according to the
  132.  * CLASS, NODE, and WILDCARD flags.  Leaf and non-leaf information is
  133.  * kept separate to speed up individual probes (non-leaf information is
  134.  * only relevant when building the stacks, but isn't relevant when
  135.  * making probes;  similarly, only non-leaf information is relevant
  136.  * when the stacks are being extended to the next widget down in the
  137.  * widget hierarchy).  Wildcard elements are handled separately from
  138.  * "exact" elements because once they appear at a particular level in
  139.  * the stack they remain active for all deeper levels;  exact elements
  140.  * are only relevant at a particular level.  For example, when searching
  141.  * for options relevant in a particular window, the entire wildcard
  142.  * stacks get checked, but only the portions of the exact stacks that
  143.  * pertain to the window's parent.  Lastly, name and class stacks are
  144.  * kept separate because different search keys are used when searching
  145.  * them;  keeping them separate speeds up the searches.
  146.  */
  147.  
  148. #define NUM_STACKS 8
  149. static ElArray *stacks[NUM_STACKS];
  150. static TkWindow *cachedWindow = NULL;    /* Lowest-level window currently
  151.                      * loaded in stacks at present. 
  152.                      * NULL means stacks have never
  153.                      * been used, or have been
  154.                      * invalidated because of a change
  155.                      * to the database. */
  156.  
  157. /*
  158.  * One of the following structures is used to keep track of each
  159.  * level in the stacks.
  160.  */
  161.  
  162. typedef struct StackLevel {
  163.     TkWindow *winPtr;        /* Window corresponding to this stack
  164.                  * level. */
  165.     int bases[NUM_STACKS];    /* For each stack, index of first
  166.                  * element on stack corresponding to
  167.                  * this level (used to restore "numUsed"
  168.                  * fields when popping out of a level. */
  169. } StackLevel;
  170.  
  171. /*
  172.  * Information about all of the stack levels that are currently
  173.  * active.  This array grows dynamically to become as large as needed.
  174.  */
  175.  
  176. static StackLevel *levels = NULL;
  177.                 /* Array describing current stack. */
  178. static int numLevels = 0;    /* Total space allocated. */
  179. static int curLevel = 0;    /* Highest level currently in use. */
  180.  
  181. /*
  182.  * The variable below is a serial number for all options entered into
  183.  * the database so far.  It increments on each addition to the option
  184.  * database.  It is used in computing option priorities, so that the
  185.  * most recent entry wins when choosing between options at the same
  186.  * priority level.
  187.  */
  188.  
  189. static int serial = 0;
  190.  
  191. /*
  192.  * Special "no match" Element to use as default for searches.
  193.  */
  194.  
  195. static Element defaultMatch;
  196.  
  197. /*
  198.  * Forward declarations for procedures defined in this file:
  199.  */
  200.  
  201. static int        AddFromString _ANSI_ARGS_((Tcl_Interp *interp,
  202.                 Tk_Window tkwin, char *string, int priority));
  203. static void        ClearOptionTree _ANSI_ARGS_((ElArray *arrayPtr));
  204. static ElArray *    ExtendArray _ANSI_ARGS_((ElArray *arrayPtr,
  205.                 Element *elPtr));
  206. static void        ExtendStacks _ANSI_ARGS_((ElArray *arrayPtr,
  207.                 int leaf));
  208. static int        GetDefaultOptions _ANSI_ARGS_((Tcl_Interp *interp,
  209.                 TkWindow *winPtr));    
  210. static ElArray *    NewArray _ANSI_ARGS_((int numEls));    
  211. static void        OptionInit _ANSI_ARGS_((TkMainInfo *mainPtr));
  212. static int        ParsePriority _ANSI_ARGS_((Tcl_Interp *interp,
  213.                 char *string));
  214. static int        ReadOptionFile _ANSI_ARGS_((Tcl_Interp *interp,
  215.                 Tk_Window tkwin, char *fileName, int priority));
  216. static void        SetupStacks _ANSI_ARGS_((TkWindow *winPtr, int leaf));
  217.  
  218. /*
  219.  *--------------------------------------------------------------
  220.  *
  221.  * Tk_AddOption --
  222.  *
  223.  *    Add a new option to the option database.
  224.  *
  225.  * Results:
  226.  *    None.
  227.  *
  228.  * Side effects:
  229.  *    Information is added to the option database.
  230.  *
  231.  *--------------------------------------------------------------
  232.  */
  233.  
  234. void
  235. Tk_AddOption(tkwin, name, value, priority)
  236.     Tk_Window tkwin;        /* Window token;  option will be associated
  237.                  * with main window for this window. */
  238.     char *name;            /* Multi-element name of option. */
  239.     char *value;        /* String value for option. */
  240.     int priority;        /* Overall priority level to use for
  241.                  * this option, such as TK_USER_DEFAULT_PRIO
  242.                  * or TK_INTERACTIVE_PRIO.  Must be between
  243.                  * 0 and TK_MAX_PRIO. */
  244. {
  245.     TkWindow *winPtr = ((TkWindow *) tkwin)->mainPtr->winPtr;
  246.     register ElArray **arrayPtrPtr;
  247.     register Element *elPtr;
  248.     Element newEl;
  249.     register char *p;
  250.     char *field;
  251.     int count, firstField, length;
  252. #define TMP_SIZE 100
  253.     char tmp[TMP_SIZE+1];
  254.  
  255.     if (winPtr->mainPtr->optionRootPtr == NULL) {
  256.     OptionInit(winPtr->mainPtr);
  257.     }
  258.     cachedWindow = NULL;    /* Invalidate the cache. */
  259.  
  260.     /*
  261.      * Compute the priority for the new element, including both the
  262.      * overall level and the serial number (to disambiguate with the
  263.      * level).
  264.      */
  265.  
  266.     if (priority < 0) {
  267.     priority = 0;
  268.     } else if (priority > TK_MAX_PRIO) {
  269.     priority = TK_MAX_PRIO;
  270.     }
  271.     newEl.priority = (priority << 24) + serial;
  272.     serial++;
  273.  
  274.     /*
  275.      * Parse the option one field at a time.
  276.      */
  277.  
  278.     arrayPtrPtr = &(((TkWindow *) tkwin)->mainPtr->optionRootPtr);
  279.     p = name;
  280.     for (firstField = 1; ; firstField = 0) {
  281.  
  282.     /*
  283.      * Scan the next field from the name and convert it to a Tk_Uid.
  284.      * Must copy the field before calling Tk_Uid, so that a terminating
  285.      * NULL may be added without modifying the source string.
  286.      */
  287.  
  288.     if (*p == '*') {
  289.         newEl.flags = WILDCARD;
  290.         p++;
  291.     } else {
  292.         newEl.flags = 0;
  293.     }
  294.     field = p;
  295.     while ((*p != 0) && (*p != '.') && (*p != '*')) {
  296.         p++;
  297.     }
  298.     length = p - field;
  299.     if (length > TMP_SIZE) {
  300.         length = TMP_SIZE;
  301.     }
  302.     strncpy(tmp, field, length);
  303.     tmp[length] = 0;
  304.     newEl.nameUid = Tk_GetUid(tmp);
  305.     if (isupper(*field)) {
  306.         newEl.flags |= CLASS;
  307.     }
  308.  
  309.     if (*p != 0) {
  310.  
  311.         /*
  312.          * New element will be a node.  If this option can't possibly
  313.          * apply to this main window, then just skip it.  Otherwise,
  314.          * add it to the parent, if it isn't already there, and descend
  315.          * into it.
  316.          */
  317.  
  318.         newEl.flags |= NODE;
  319.         if (firstField && !(newEl.flags & WILDCARD)
  320.             && (newEl.nameUid != winPtr->nameUid)
  321.             && (newEl.nameUid != winPtr->classUid)) {
  322.         return;
  323.         }
  324.         for (elPtr = (*arrayPtrPtr)->els, count = (*arrayPtrPtr)->numUsed;
  325.             ; elPtr++, count--) {
  326.         if (count == 0) {
  327.             newEl.child.arrayPtr = NewArray(5);
  328.             *arrayPtrPtr = ExtendArray(*arrayPtrPtr, &newEl);
  329.             arrayPtrPtr = &((*arrayPtrPtr)->nextToUse[-1].child.arrayPtr);
  330.             break;
  331.         }
  332.         if ((elPtr->nameUid == newEl.nameUid)
  333.             && (elPtr->flags == newEl.flags)) {
  334.             arrayPtrPtr = &(elPtr->child.arrayPtr);
  335.             break;
  336.         }
  337.         }
  338.         if (*p == '.') {
  339.         p++;
  340.         }
  341.     } else {
  342.  
  343.         /*
  344.          * New element is a leaf.  Add it to the parent, if it isn't
  345.          * already there.  If it exists already, keep whichever value
  346.          * has highest priority.
  347.          */
  348.  
  349.         newEl.child.valueUid = Tk_GetUid(value);
  350.         for (elPtr = (*arrayPtrPtr)->els, count = (*arrayPtrPtr)->numUsed;
  351.             ; elPtr++, count--) {
  352.         if (count == 0) {
  353.             *arrayPtrPtr = ExtendArray(*arrayPtrPtr, &newEl);
  354.             return;
  355.         }
  356.         if ((elPtr->nameUid == newEl.nameUid)
  357.             && (elPtr->flags == newEl.flags)) {
  358.             if (elPtr->priority < newEl.priority) {
  359.             elPtr->priority = newEl.priority;
  360.             elPtr->child.valueUid = newEl.child.valueUid;
  361.             }
  362.             return;
  363.         }
  364.         }
  365.     }
  366.     }
  367. }
  368.  
  369. /*
  370.  *--------------------------------------------------------------
  371.  *
  372.  * Tk_GetOption --
  373.  *
  374.  *    Retrieve an option from the option database.
  375.  *
  376.  * Results:
  377.  *    The return value is the value specified in the option
  378.  *    database for the given name and class on the given
  379.  *    window.  If there is nothing specified in the database
  380.  *    for that option, then NULL is returned.
  381.  *
  382.  * Side effects:
  383.  *    The internal caches used to speed up option mapping
  384.  *    may be modified, if this tkwin is different from the
  385.  *    last tkwin used for option retrieval.
  386.  *
  387.  *--------------------------------------------------------------
  388.  */
  389.  
  390. Tk_Uid
  391. Tk_GetOption(tkwin, name, className)
  392.     Tk_Window tkwin;        /* Token for window that option is
  393.                  * associated with. */
  394.     char *name;            /* Name of option. */
  395.     char *className;        /* Class of option.  NULL means there
  396.                  * is no class for this option:  just
  397.                  * check for name. */
  398. {
  399.     Tk_Uid nameId, classId;
  400.     register Element *elPtr, *bestPtr;
  401.     register int count;
  402.  
  403.     /*
  404.      * Note:  no need to call OptionInit here:  it will be done by
  405.      * the SetupStacks call below (squeeze out those nanoseconds).
  406.      */
  407.  
  408.     if (tkwin != (Tk_Window) cachedWindow) {
  409.     SetupStacks((TkWindow *) tkwin, 1);
  410.     }
  411.  
  412.     nameId = Tk_GetUid(name);
  413.     bestPtr = &defaultMatch;
  414.     for (elPtr = stacks[EXACT_LEAF_NAME]->els,
  415.         count = stacks[EXACT_LEAF_NAME]->numUsed; count > 0;
  416.         elPtr++, count--) {
  417.     if ((elPtr->nameUid == nameId)
  418.         && (elPtr->priority > bestPtr->priority)) {
  419.         bestPtr = elPtr;
  420.     }
  421.     }
  422.     for (elPtr = stacks[WILDCARD_LEAF_NAME]->els,
  423.         count = stacks[WILDCARD_LEAF_NAME]->numUsed; count > 0;
  424.         elPtr++, count--) {
  425.     if ((elPtr->nameUid == nameId)
  426.         && (elPtr->priority > bestPtr->priority)) {
  427.         bestPtr = elPtr;
  428.     }
  429.     }
  430.     if (className != NULL) {
  431.     classId = Tk_GetUid(className);
  432.     for (elPtr = stacks[EXACT_LEAF_CLASS]->els,
  433.         count = stacks[EXACT_LEAF_CLASS]->numUsed; count > 0;
  434.         elPtr++, count--) {
  435.         if ((elPtr->nameUid == classId)
  436.             && (elPtr->priority > bestPtr->priority)) {
  437.         bestPtr = elPtr;
  438.         }
  439.     }
  440.     for (elPtr = stacks[WILDCARD_LEAF_CLASS]->els,
  441.         count = stacks[WILDCARD_LEAF_CLASS]->numUsed; count > 0;
  442.         elPtr++, count--) {
  443.         if ((elPtr->nameUid == classId)
  444.             && (elPtr->priority > bestPtr->priority)) {
  445.         bestPtr = elPtr;
  446.         }
  447.     }
  448.     }
  449.     return bestPtr->child.valueUid;
  450. }
  451.  
  452. /*
  453.  *--------------------------------------------------------------
  454.  *
  455.  * Tk_OptionCmd --
  456.  *
  457.  *    This procedure is invoked to process the "option" Tcl command.
  458.  *    See the user documentation for details on what it does.
  459.  *
  460.  * Results:
  461.  *    A standard Tcl result.
  462.  *
  463.  * Side effects:
  464.  *    See the user documentation.
  465.  *
  466.  *--------------------------------------------------------------
  467.  */
  468.  
  469. int
  470. Tk_OptionCmd(clientData, interp, argc, argv)
  471.     ClientData clientData;    /* Main window associated with
  472.                  * interpreter. */
  473.     Tcl_Interp *interp;        /* Current interpreter. */
  474.     int argc;            /* Number of arguments. */
  475.     char **argv;        /* Argument strings. */
  476. {
  477.     Tk_Window tkwin = (Tk_Window) clientData;
  478.     int length;
  479.     char c;
  480.  
  481.     if (argc < 2) {
  482.     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
  483.         " cmd arg ?arg ...?\"", (char *) NULL);
  484.     return TCL_ERROR;
  485.     }
  486.     c = argv[1][0];
  487.     length = strlen(argv[1]);
  488.     if ((c == 'a') && (strncmp(argv[1], "add", length) == 0)) {
  489.     int priority;
  490.  
  491.     if ((argc != 4) && (argc != 5)) {
  492.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  493.             argv[0], " add pattern value ?priority?\"", (char *) NULL);
  494.         return TCL_ERROR;
  495.     }
  496.     if (argc == 4) {
  497.         priority = TK_INTERACTIVE_PRIO;
  498.     } else {
  499.         priority = ParsePriority(interp, argv[4]);
  500.         if (priority < 0) {
  501.         return TCL_ERROR;
  502.         }
  503.     }
  504.     Tk_AddOption(tkwin, argv[2], argv[3], priority);
  505.     return TCL_OK;
  506.     } else if ((c == 'c') && (strncmp(argv[1], "clear", length) == 0)) {
  507.     TkMainInfo *mainPtr;
  508.  
  509.     if (argc != 2) {
  510.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  511.             argv[0], " clear\"", (char *) NULL);
  512.         return TCL_ERROR;
  513.     }
  514.     mainPtr = ((TkWindow *) tkwin)->mainPtr;
  515.     if (mainPtr->optionRootPtr != NULL) {
  516.         ClearOptionTree(mainPtr->optionRootPtr);
  517.         mainPtr->optionRootPtr = NULL;
  518.     }
  519.     cachedWindow = NULL;
  520.     return TCL_OK;
  521.     } else if ((c == 'g') && (strncmp(argv[1], "get", length) == 0)) {
  522.     Tk_Window window;
  523.     Tk_Uid value;
  524.  
  525.     if (argc != 5) {
  526.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  527.             argv[0], " get window name class\"", (char *) NULL);
  528.         return TCL_ERROR;
  529.     }
  530.     window = Tk_NameToWindow(interp, argv[2], tkwin);
  531.     if (window == NULL) {
  532.         return TCL_ERROR;
  533.     }
  534.     value = Tk_GetOption(window, argv[3], argv[4]);
  535.     if (value != NULL) {
  536.         interp->result = value;
  537.     }
  538.     return TCL_OK;
  539.     } else if ((c == 'r') && (strncmp(argv[1], "readfile", length) == 0)) {
  540.     int priority;
  541.  
  542.     if ((argc != 3) && (argc != 4)) {
  543.         Tcl_AppendResult(interp, "wrong # args:  should be \"",
  544.             argv[0], " readfile fileName ?priority?\"",
  545.             (char *) NULL);
  546.         return TCL_ERROR;
  547.     }
  548.     if (argc == 4) {
  549.         priority = ParsePriority(interp, argv[3]);
  550.         if (priority < 0) {
  551.         return TCL_ERROR;
  552.         }
  553.     } else {
  554.         priority = TK_INTERACTIVE_PRIO;
  555.     }
  556.     return ReadOptionFile(interp, tkwin, argv[2], priority);
  557.     } else {
  558.     Tcl_AppendResult(interp, "bad option \"", argv[1],
  559.         "\": must be add, clear, get, or readfile", (char *) NULL);
  560.     return TCL_ERROR;
  561.     }
  562. }
  563.  
  564. /*
  565.  *--------------------------------------------------------------
  566.  *
  567.  * TkOptionDeadWindow --
  568.  *
  569.  *    This procedure is called whenever a window is deleted.
  570.  *    It cleans up any option-related stuff associated with
  571.  *    the window.
  572.  *
  573.  * Results:
  574.  *    None.
  575.  *
  576.  * Side effects:
  577.  *    Option-related resources are freed.  See code below
  578.  *    for details.
  579.  *
  580.  *--------------------------------------------------------------
  581.  */
  582.  
  583. void
  584. TkOptionDeadWindow(winPtr)
  585.     register TkWindow *winPtr;        /* Window to be cleaned up. */
  586. {
  587.     /*
  588.      * If this window is in the option stacks, then clear the stacks.
  589.      */
  590.  
  591.     if (winPtr->optionLevel != -1) {
  592.     int i;
  593.  
  594.     for (i = 1; i <= curLevel; i++) {
  595.         levels[curLevel].winPtr->optionLevel = -1;
  596.     }
  597.     curLevel = 0;
  598.     cachedWindow = NULL;
  599.     }
  600.  
  601.     /*
  602.      * If this window was a main window, then delete its option
  603.      * database.
  604.      */
  605.  
  606.     if ((winPtr->mainPtr->winPtr == winPtr)
  607.         && (winPtr->mainPtr->optionRootPtr != NULL)) {
  608.     ClearOptionTree(winPtr->mainPtr->optionRootPtr);
  609.     winPtr->mainPtr->optionRootPtr = NULL;
  610.     }
  611. }
  612.  
  613. /*
  614.  *----------------------------------------------------------------------
  615.  *
  616.  * ParsePriority --
  617.  *
  618.  *    Parse a string priority value.
  619.  *
  620.  * Results:
  621.  *    The return value is the integer priority level corresponding
  622.  *    to string, or -1 if string doesn't point to a valid priority level.
  623.  *    In this case, an error message is left in interp->result.
  624.  *
  625.  * Side effects:
  626.  *    None.
  627.  *
  628.  *----------------------------------------------------------------------
  629.  */
  630.  
  631. static int
  632. ParsePriority(interp, string)
  633.     Tcl_Interp *interp;        /* Interpreter to use for error reporting. */
  634.     char *string;        /* Describes a priority level, either
  635.                  * symbolically or numerically. */
  636. {
  637.     char c;
  638.     int length, priority;
  639.  
  640.     c = string[0];
  641.     length = strlen(string);
  642.     if ((c == 'w')
  643.         && (strncmp(string, "widgetDefault", length) == 0)) {
  644.     return TK_WIDGET_DEFAULT_PRIO;
  645.     } else if ((c == 's')
  646.         && (strncmp(string, "startupFile", length) == 0)) {
  647.     return TK_STARTUP_FILE_PRIO;
  648.     } else if ((c == 'u')
  649.         && (strncmp(string, "userDefault", length) == 0)) {
  650.     return TK_USER_DEFAULT_PRIO;
  651.     } else if ((c == 'i')
  652.         && (strncmp(string, "interactive", length) == 0)) {
  653.     return TK_INTERACTIVE_PRIO;
  654.     } else {
  655.     char *end;
  656.  
  657.     priority = strtoul(string, &end, 0);
  658.     if ((end == string) || (*end != 0) || (priority < 0)
  659.         || (priority > 100)) {
  660.         Tcl_AppendResult(interp,  "bad priority level \"", string,
  661.             "\": must be widgetDefault, startupFile, userDefault, ",
  662.             "interactive, or a number between 0 and 100",
  663.             (char *) NULL);
  664.         return -1;
  665.     }
  666.     }
  667.     return priority;
  668. }
  669.  
  670. /*
  671.  *----------------------------------------------------------------------
  672.  *
  673.  * AddFromString --
  674.  *
  675.  *    Given a string containing lines in the standard format for
  676.  *    X resources (see other documentation for details on what this
  677.  *    is), parse the resource specifications and enter them as options
  678.  *    for tkwin's main window.
  679.  *
  680.  * Results:
  681.  *    The return value is a standard Tcl return code.  In the case of
  682.  *    an error in parsing string, TCL_ERROR will be returned and an
  683.  *    error message will be left in interp->result.  The memory at
  684.  *    string is totally trashed by this procedure.  If you care about
  685.  *    its contents, make a copy before calling here.
  686.  *
  687.  * Side effects:
  688.  *    None.
  689.  *
  690.  *----------------------------------------------------------------------
  691.  */
  692.  
  693. static int
  694. AddFromString(interp, tkwin, string, priority)
  695.     Tcl_Interp *interp;        /* Interpreter to use for reporting results. */
  696.     Tk_Window tkwin;        /* Token for window:  options are entered
  697.                  * for this window's main window. */
  698.     char *string;        /* String containing option specifiers. */
  699.     int priority;        /* Priority level to use for options in
  700.                  * this string, such as TK_USER_DEFAULT_PRIO
  701.                  * or TK_INTERACTIVE_PRIO.  Must be between
  702.                  * 0 and TK_MAX_PRIO. */
  703. {
  704.     register char *src, *dst;
  705.     char *name, *value;
  706.     int lineNum;
  707.  
  708.     src = string;
  709.     lineNum = 1;
  710.     while (1) {
  711.  
  712.     /*
  713.      * Skip leading white space and empty lines and comment lines, and
  714.      * check for the end of the spec.
  715.      */
  716.  
  717.     while ((*src == ' ') || (*src == '\t')) {
  718.         src++;
  719.     }
  720.     if ((*src == '#') || (*src == '!')) {
  721.         do {
  722.         src++;
  723.         if ((src[0] == '\\') && (src[1] == '\n')) {
  724.             src += 2;
  725.             lineNum++;
  726.         }
  727.         } while ((*src != '\n') && (*src != 0));
  728.     }
  729.     if (*src == '\n') {
  730.         src++;
  731.         lineNum++;
  732.         continue;
  733.     } 
  734.     if (*src == '\0') {
  735.         break;
  736.     }
  737.  
  738.     /*
  739.      * Parse off the option name, collapsing out backslash-newline
  740.      * sequences of course.
  741.      */
  742.  
  743.     dst = name = src;
  744.     while (*src != ':') {
  745.         if ((*src == '\0') || (*src == '\n')) {
  746.         sprintf(interp->result, "missing colon on line %d",
  747.             lineNum);
  748.         return TCL_ERROR;
  749.         }
  750.         if ((src[0] == '\\') && (src[1] == '\n')) {
  751.         src += 2;
  752.         lineNum++;
  753.         } else {
  754.         *dst = *src;
  755.         dst++;
  756.         src++;
  757.         }
  758.     }
  759.  
  760.     /*
  761.      * Eliminate trailing white space on the name, and null-terminate
  762.      * it.
  763.      */
  764.  
  765.     while ((dst != name) && ((dst[-1] == ' ') || (dst[-1] == '\t'))) {
  766.         dst--;
  767.     }
  768.     *dst = '\0';
  769.  
  770.     /*
  771.      * Skip white space between the name and the value.
  772.      */
  773.  
  774.     src++;
  775.     while ((*src == ' ') || (*src == '\t')) {
  776.         src++;
  777.     }
  778.     if (*src == '\0') {
  779.         sprintf(interp->result, "missing value on line %d", lineNum);
  780.         return TCL_ERROR;
  781.     }
  782.  
  783.     /*
  784.      * Parse off the value, squeezing out backslash-newline sequences
  785.      * along the way.
  786.      */
  787.  
  788.     dst = value = src;
  789.     while (*src != '\n') {
  790.         if (*src == '\0') {
  791.         sprintf(interp->result, "missing newline on line %d",
  792.             lineNum);
  793.         return TCL_ERROR;
  794.         }
  795.         if ((src[0] == '\\') && (src[1] == '\n')) {
  796.         src += 2;
  797.         lineNum++;
  798.         } else {
  799.         *dst = *src;
  800.         dst++;
  801.         src++;
  802.         }
  803.     }
  804.     *dst = 0;
  805.  
  806.     /*
  807.      * Enter the option into the database.
  808.      */
  809.  
  810.     Tk_AddOption(tkwin, name, value, priority);
  811.     src++;
  812.     lineNum++;
  813.     }
  814.     return TCL_OK;
  815. }
  816.  
  817. /*
  818.  *----------------------------------------------------------------------
  819.  *
  820.  * ReadOptionFile --
  821.  *
  822.  *     Read a file of options ("resources" in the old X terminology)
  823.  *    and load them into the option database.
  824.  *
  825.  * Results:
  826.  *    The return value is a standard Tcl return code.  In the case of
  827.  *    an error in parsing string, TCL_ERROR will be returned and an
  828.  *    error message will be left in interp->result.
  829.  *
  830.  * Side effects:
  831.  *    None.
  832.  *
  833.  *----------------------------------------------------------------------
  834.  */
  835.  
  836. static int
  837. ReadOptionFile(interp, tkwin, fileName, priority)
  838.     Tcl_Interp *interp;        /* Interpreter to use for reporting results. */
  839.     Tk_Window tkwin;        /* Token for window:  options are entered
  840.                  * for this window's main window. */
  841.     char *fileName;        /* Name of file containing options. */
  842.     int priority;        /* Priority level to use for options in
  843.                  * this file, such as TK_USER_DEFAULT_PRIO
  844.                  * or TK_INTERACTIVE_PRIO.  Must be between
  845.                  * 0 and TK_MAX_PRIO. */
  846. {
  847.     char *realName, *buffer;
  848.     int fileId, result;
  849.     struct stat statBuf;
  850.  
  851.     realName = Tcl_TildeSubst(interp, fileName);
  852.     if (fileName == NULL) {
  853.     return TCL_ERROR;
  854.     }
  855.     fileId = open(realName, O_RDONLY, 0);
  856.     if (fileId < 0) {
  857.     Tcl_AppendResult(interp, "couldn't read file \"", fileName, "\"",
  858.         (char *) NULL);
  859.     return TCL_ERROR;
  860.     }
  861.     if (fstat(fileId, &statBuf) == -1) {
  862.     Tcl_AppendResult(interp, "couldn't stat file \"", fileName, "\"",
  863.         (char *) NULL);
  864.     close(fileId);
  865.     return TCL_ERROR;
  866.     }
  867.     buffer = (char *) ckalloc((unsigned) statBuf.st_size+1);
  868.     if (read(fileId, buffer, (int) statBuf.st_size) != statBuf.st_size) {
  869.     Tcl_AppendResult(interp, "error reading file \"", fileName, "\"",
  870.         (char *) NULL);
  871.     close(fileId);
  872.     return TCL_ERROR;
  873.     }
  874.     close(fileId);
  875.     buffer[statBuf.st_size] = 0;
  876.     result = AddFromString(interp, tkwin, buffer, priority);
  877.     ckfree(buffer);
  878.     return result;
  879. }
  880.  
  881. /*
  882.  *--------------------------------------------------------------
  883.  *
  884.  * NewArray --
  885.  *
  886.  *    Create a new ElArray structure of a given size.
  887.  *
  888.  * Results:
  889.  *    The return value is a pointer to a properly initialized
  890.  *    element array with "numEls" space.  The array is marked
  891.  *    as having no active elements.
  892.  *
  893.  * Side effects:
  894.  *    Memory is allocated.
  895.  *
  896.  *--------------------------------------------------------------
  897.  */
  898.  
  899. static ElArray *
  900. NewArray(numEls)
  901.     int numEls;            /* How many elements of space to allocate. */
  902. {
  903.     register ElArray *arrayPtr;
  904.  
  905.     arrayPtr = (ElArray *) ckalloc(EL_ARRAY_SIZE(numEls));
  906.     arrayPtr->arraySize = numEls;
  907.     arrayPtr->numUsed = 0;
  908.     arrayPtr->nextToUse = arrayPtr->els;
  909.     return arrayPtr;
  910. }
  911.  
  912. /*
  913.  *--------------------------------------------------------------
  914.  *
  915.  * ExtendArray --
  916.  *
  917.  *    Add a new element to an array, extending the array if
  918.  *    necessary.
  919.  *
  920.  * Results:
  921.  *    The return value is a pointer to the new array, which
  922.  *    will be different from arrayPtr if the array got expanded.
  923.  *
  924.  * Side effects:
  925.  *    Memory may be allocated or freed.
  926.  *
  927.  *--------------------------------------------------------------
  928.  */
  929.  
  930. static ElArray *
  931. ExtendArray(arrayPtr, elPtr)
  932.     register ElArray *arrayPtr;        /* Array to be extended. */
  933.     register Element *elPtr;        /* Element to be copied into array. */
  934. {
  935.     /*
  936.      * If the current array has filled up, make it bigger.
  937.      */
  938.  
  939.     if (arrayPtr->numUsed >= arrayPtr->arraySize) {
  940.     register ElArray *newPtr;
  941.  
  942.     newPtr = (ElArray *) ckalloc(EL_ARRAY_SIZE(2*arrayPtr->arraySize));
  943.     newPtr->arraySize = 2*arrayPtr->arraySize;
  944.     newPtr->numUsed = arrayPtr->numUsed;
  945.     newPtr->nextToUse = &newPtr->els[newPtr->numUsed];
  946.     memcpy((VOID *) newPtr->els, (VOID *) arrayPtr->els,
  947.         (arrayPtr->arraySize*sizeof(Element)));
  948.     ckfree((char *) arrayPtr);
  949.     arrayPtr = newPtr;
  950.     }
  951.  
  952.     *arrayPtr->nextToUse = *elPtr;
  953.     arrayPtr->nextToUse++;
  954.     arrayPtr->numUsed++;
  955.     return arrayPtr;
  956. }
  957.  
  958. /*
  959.  *--------------------------------------------------------------
  960.  *
  961.  * SetupStacks --
  962.  *
  963.  *    Arrange the stacks so that they cache all the option
  964.  *    information for a particular window.
  965.  *
  966.  * Results:
  967.  *    None.
  968.  *
  969.  * Side effects:
  970.  *    The stacks are modified to hold information for tkwin
  971.  *    and all its ancestors in the window hierarchy.
  972.  *
  973.  *--------------------------------------------------------------
  974.  */
  975.  
  976. static void
  977. SetupStacks(winPtr, leaf)
  978.     TkWindow *winPtr;        /* Window for which information is to
  979.                  * be cached. */
  980.     int leaf;            /* Non-zero means this is the leaf
  981.                  * window being probed.  Zero means this
  982.                  * is an ancestor of the desired leaf. */
  983. {
  984.     int level, i, *iPtr;
  985.     register StackLevel *levelPtr;
  986.     register ElArray *arrayPtr;
  987.  
  988.     /*
  989.      * The following array defines the order in which the current
  990.      * stacks are searched to find matching entries to add to the
  991.      * stacks.  Given the current priority-based scheme, the order
  992.      * below is no longer relevant;  all that matters is that an
  993.      * element is on the list *somewhere*.  The ordering is a relic
  994.      * of the old days when priorities were determined differently.
  995.      */
  996.  
  997.     static int searchOrder[] = {WILDCARD_NODE_CLASS, WILDCARD_NODE_NAME,
  998.         EXACT_NODE_CLASS, EXACT_NODE_NAME, -1};
  999.  
  1000.     if (winPtr->mainPtr->optionRootPtr == NULL) {
  1001.     OptionInit(winPtr->mainPtr);
  1002.     }
  1003.  
  1004.     /*
  1005.      * Step 1:  make sure that options are cached for this window's
  1006.      * parent.
  1007.      */
  1008.  
  1009.     if (winPtr->parentPtr != NULL) {
  1010.     level = winPtr->parentPtr->optionLevel;
  1011.     if ((level == -1) || (cachedWindow == NULL)) {
  1012.         SetupStacks(winPtr->parentPtr, 0);
  1013.         level = winPtr->parentPtr->optionLevel;
  1014.     }
  1015.     level++;
  1016.     } else {
  1017.     level = 1;
  1018.     }
  1019.  
  1020.     /*
  1021.      * Step 2:  pop extra unneeded information off the stacks and
  1022.      * mark those windows as no longer having cached information.
  1023.      */
  1024.  
  1025.     if (curLevel >= level) {
  1026.     while (curLevel >= level) {
  1027.         levels[curLevel].winPtr->optionLevel = -1;
  1028.         curLevel--;
  1029.     }
  1030.     levelPtr = &levels[level];
  1031.     for (i = 0; i < NUM_STACKS; i++) {
  1032.         arrayPtr = stacks[i];
  1033.         arrayPtr->numUsed = levelPtr->bases[i];
  1034.         arrayPtr->nextToUse = &arrayPtr->els[arrayPtr->numUsed];
  1035.     }
  1036.     }
  1037.     curLevel = winPtr->optionLevel = level;
  1038.  
  1039.     /*
  1040.      * Step 3:  if the root database information isn't loaded or
  1041.      * isn't valid, initialize level 0 of the stack from the
  1042.      * database root (this only happens if winPtr is a main window).
  1043.      */
  1044.  
  1045.     if ((curLevel == 1)
  1046.         && ((cachedWindow == NULL)
  1047.         || (cachedWindow->mainPtr != winPtr->mainPtr))) {
  1048.     for (i = 0; i < NUM_STACKS; i++) {
  1049.         arrayPtr = stacks[i];
  1050.         arrayPtr->numUsed = 0;
  1051.         arrayPtr->nextToUse = arrayPtr->els;
  1052.     }
  1053.     ExtendStacks(winPtr->mainPtr->optionRootPtr, 0);
  1054.     }
  1055.  
  1056.     /*
  1057.      * Step 4: create a new stack level;  grow the level array if
  1058.      * we've run out of levels.  Clear the stacks for EXACT_LEAF_NAME
  1059.      * and EXACT_LEAF_CLASS (anything that was there is of no use
  1060.      * any more).
  1061.      */
  1062.  
  1063.     if (curLevel >= numLevels) {
  1064.     StackLevel *newLevels;
  1065.  
  1066.     newLevels = (StackLevel *) ckalloc((unsigned)
  1067.         (numLevels*2*sizeof(StackLevel)));
  1068.     memcpy((VOID *) newLevels, (VOID *) levels,
  1069.         (numLevels*sizeof(StackLevel)));
  1070.     ckfree((char *) levels);
  1071.     numLevels *= 2;
  1072.     levels = newLevels;
  1073.     }
  1074.     levelPtr = &levels[curLevel];
  1075.     levelPtr->winPtr = winPtr;
  1076.     arrayPtr = stacks[EXACT_LEAF_NAME];
  1077.     arrayPtr->numUsed = 0;
  1078.     arrayPtr->nextToUse = arrayPtr->els;
  1079.     arrayPtr = stacks[EXACT_LEAF_CLASS];
  1080.     arrayPtr->numUsed = 0;
  1081.     arrayPtr->nextToUse = arrayPtr->els;
  1082.     levelPtr->bases[EXACT_LEAF_NAME] = stacks[EXACT_LEAF_NAME]->numUsed;
  1083.     levelPtr->bases[EXACT_LEAF_CLASS] = stacks[EXACT_LEAF_CLASS]->numUsed;
  1084.     levelPtr->bases[EXACT_NODE_NAME] = stacks[EXACT_NODE_NAME]->numUsed;
  1085.     levelPtr->bases[EXACT_NODE_CLASS] = stacks[EXACT_NODE_CLASS]->numUsed;
  1086.     levelPtr->bases[WILDCARD_LEAF_NAME] = stacks[WILDCARD_LEAF_NAME]->numUsed;
  1087.     levelPtr->bases[WILDCARD_LEAF_CLASS] = stacks[WILDCARD_LEAF_CLASS]->numUsed;
  1088.     levelPtr->bases[WILDCARD_NODE_NAME] = stacks[WILDCARD_NODE_NAME]->numUsed;
  1089.     levelPtr->bases[WILDCARD_NODE_CLASS] = stacks[WILDCARD_NODE_CLASS]->numUsed;
  1090.  
  1091.  
  1092.     /*
  1093.      * Step 5: scan the current stack level looking for matches to this
  1094.      * window's name or class;  where found, add new information to the
  1095.      * stacks.
  1096.      */
  1097.  
  1098.     for (iPtr = searchOrder; *iPtr != -1; iPtr++) {
  1099.     register Element *elPtr;
  1100.     int count;
  1101.     Tk_Uid id;
  1102.  
  1103.     i = *iPtr;
  1104.     if (i & CLASS) {
  1105.         id = winPtr->classUid;
  1106.     } else {
  1107.         id = winPtr->nameUid;
  1108.     }
  1109.     elPtr = stacks[i]->els;
  1110.     count = levelPtr->bases[i];
  1111.  
  1112.     /*
  1113.      * For wildcard stacks, check all entries;  for non-wildcard
  1114.      * stacks, only check things that matched in the parent.
  1115.      */
  1116.  
  1117.     if (!(i & WILDCARD)) {
  1118.         elPtr += levelPtr[-1].bases[i];
  1119.         count -= levelPtr[-1].bases[i];
  1120.     }
  1121.     for ( ; count > 0; elPtr++, count--) {
  1122.         if (elPtr->nameUid != id) {
  1123.         continue;
  1124.         }
  1125.         ExtendStacks(elPtr->child.arrayPtr, leaf);
  1126.     }
  1127.     }
  1128.     cachedWindow = winPtr;
  1129. }
  1130.  
  1131. /*
  1132.  *--------------------------------------------------------------
  1133.  *
  1134.  * ExtendStacks --
  1135.  *
  1136.  *    Given an element array, copy all the elements from the
  1137.  *    array onto the system stacks (except for irrelevant leaf
  1138.  *    elements).
  1139.  *
  1140.  * Results:
  1141.  *    None.
  1142.  *
  1143.  * Side effects:
  1144.  *    The option stacks are extended.
  1145.  *
  1146.  *--------------------------------------------------------------
  1147.  */
  1148.  
  1149. static void
  1150. ExtendStacks(arrayPtr, leaf)
  1151.     ElArray *arrayPtr;        /* Array of elements to copy onto stacks. */
  1152.     int leaf;            /* If zero, then don't copy exact leaf
  1153.                  * elements. */
  1154. {
  1155.     register int count;
  1156.     register Element *elPtr;
  1157.  
  1158.     for (elPtr = arrayPtr->els, count = arrayPtr->numUsed;
  1159.         count > 0; elPtr++, count--) {
  1160.     if (!(elPtr->flags & (NODE|WILDCARD)) && !leaf) {
  1161.         continue;
  1162.     }
  1163.     stacks[elPtr->flags] = ExtendArray(stacks[elPtr->flags], elPtr);
  1164.     }
  1165. }
  1166.  
  1167. /*
  1168.  *--------------------------------------------------------------
  1169.  *
  1170.  * OptionInit --
  1171.  *
  1172.  *    Initialize data structures for option handling.
  1173.  *
  1174.  * Results:
  1175.  *    None.
  1176.  *
  1177.  * Side effects:
  1178.  *    Option-related data structures get initialized.
  1179.  *
  1180.  *--------------------------------------------------------------
  1181.  */
  1182.  
  1183. static void
  1184. OptionInit(mainPtr)
  1185.     register TkMainInfo *mainPtr;    /* Top-level information about
  1186.                      * window that isn't initialized
  1187.                      * yet. */
  1188. {
  1189.     int i;
  1190.     Tcl_Interp *interp;
  1191.  
  1192.     /*
  1193.      * First, once-only initialization.
  1194.      */
  1195.  
  1196.     if (numLevels == 0) {
  1197.  
  1198.     numLevels = 5;
  1199.     levels = (StackLevel *) ckalloc((unsigned) (5*sizeof(StackLevel)));
  1200.     for (i = 0; i < NUM_STACKS; i++) {
  1201.         stacks[i] = NewArray(10);
  1202.         levels[0].bases[i] = 0;
  1203.     }
  1204.     
  1205.     defaultMatch.nameUid = NULL;
  1206.     defaultMatch.child.valueUid = NULL;
  1207.     defaultMatch.priority = -1;
  1208.     defaultMatch.flags = 0;
  1209.     }
  1210.  
  1211.     /*
  1212.      * Then, per-main-window initialization.  Create and delete dummy
  1213.      * interpreter for message logging.
  1214.      */
  1215.  
  1216.     mainPtr->optionRootPtr = NewArray(20);
  1217.     interp = Tcl_CreateInterp();
  1218.     (void) GetDefaultOptions(interp, mainPtr->winPtr);
  1219.     Tcl_DeleteInterp(interp);
  1220. }
  1221.  
  1222. /*
  1223.  *--------------------------------------------------------------
  1224.  *
  1225.  * ClearOptionTree --
  1226.  *
  1227.  *    This procedure is called to erase everything in a
  1228.  *    hierarchical option database.
  1229.  *
  1230.  * Results:
  1231.  *    None.
  1232.  *
  1233.  * Side effects:
  1234.  *    All the options associated with arrayPtr are deleted,
  1235.  *    along with all option subtrees.  The space pointed to
  1236.  *    by arrayPtr is freed.
  1237.  *
  1238.  *--------------------------------------------------------------
  1239.  */
  1240.  
  1241. static void
  1242. ClearOptionTree(arrayPtr)
  1243.     ElArray *arrayPtr;        /* Array of options;  delete everything
  1244.                  * referred to recursively by this. */
  1245. {
  1246.     register Element *elPtr;
  1247.     int count;
  1248.  
  1249.     for (count = arrayPtr->numUsed, elPtr = arrayPtr->els;  count > 0;
  1250.         count--, elPtr++) {
  1251.     if (elPtr->flags & NODE) {
  1252.         ClearOptionTree(elPtr->child.arrayPtr);
  1253.     }
  1254.     }
  1255.     ckfree((char *) arrayPtr);
  1256. }
  1257.  
  1258. /*
  1259.  *--------------------------------------------------------------
  1260.  *
  1261.  * GetDefaultOptions --
  1262.  *
  1263.  *    This procedure is invoked to load the default set of options
  1264.  *    for a window.
  1265.  *
  1266.  * Results:
  1267.  *    None.
  1268.  *
  1269.  * Side effects:
  1270.  *    Options are added to those for winPtr's main window.  If
  1271.  *    there exists a RESOURCE_MANAGER proprety for winPtr's
  1272.  *    display, that is used.  Otherwise, the .Xdefaults file in
  1273.  *    the user's home directory is used.
  1274.  *
  1275.  *--------------------------------------------------------------
  1276.  */
  1277.  
  1278. static int
  1279. GetDefaultOptions(interp, winPtr)
  1280.     Tcl_Interp *interp;        /* Interpreter to use for error reporting. */
  1281.     TkWindow *winPtr;        /* Fetch option defaults for main window
  1282.                  * associated with this. */
  1283. {
  1284.     char *regProp, *home, *fileName;
  1285.     int result, actualFormat;
  1286.     unsigned long numItems, bytesAfter;
  1287.     Atom actualType;
  1288.  
  1289.     /*
  1290.      * Try the RESOURCE_MANAGER property on the root window first.
  1291.      */
  1292.  
  1293.     regProp = NULL;
  1294.     result = XGetWindowProperty(winPtr->display,
  1295.         DefaultRootWindow(winPtr->display),
  1296.         XA_RESOURCE_MANAGER, 0, 100000,
  1297.         False, XA_STRING, &actualType, &actualFormat,
  1298.         &numItems, &bytesAfter, (unsigned char **) ®Prop);
  1299.  
  1300.     if ((result == Success) && (actualType == XA_STRING)
  1301.         && (actualFormat == 8)) {
  1302.     result = AddFromString(interp, (Tk_Window) winPtr, regProp,
  1303.         TK_USER_DEFAULT_PRIO);
  1304.     XFree(regProp);
  1305.     return result;
  1306.     }
  1307.  
  1308.     /*
  1309.      * No luck there.  Try a .Xdefaults file in the user's home
  1310.      * directory.
  1311.      */
  1312.  
  1313.     if (regProp != NULL) {
  1314.     XFree(regProp);
  1315.     }
  1316.     home = getenv("HOME");
  1317.     if (home == NULL) {
  1318.     sprintf(interp->result,
  1319.         "no RESOURCE_MANAGER property and no HOME envariable");
  1320.     return TCL_ERROR;
  1321.     }
  1322.     fileName = (char *) ckalloc((unsigned) (strlen(home) + 20));
  1323.     sprintf(fileName, "%s/.Xdefaults", home);
  1324.     result = ReadOptionFile(interp, (Tk_Window) winPtr, fileName,
  1325.         TK_USER_DEFAULT_PRIO);
  1326.     ckfree(fileName);
  1327.     return result;
  1328. }
  1329.